package main.工具.类生成器;

import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.IOException;
import java.util.List;

import main.工具.公用.FileUtil;
import main.工具.解析.java.类结构提取器;
import main.工具.解析.java.类结构提取器.类型结构;

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.WildcardType;

public class 源码生成器 {
  private static final ASTParser 语法解析器 = ASTParser.newParser(AST.JLS8);
  private static final AST ast = AST.newAST(AST.JLS8);
  private static final 类结构提取器 提取器 = new 类结构提取器();

  public static void main(String[] args) throws Exception {
    if (args.length != 1) {
      System.out.println("需要JDK源码路径作为唯一参数");
      return;
    }

    walkFile(new File(args[0]));
  }

  private static void walkFile(File path) throws Exception {
    if (path.isFile()) {
      if (path.getName().endsWith(".java")) {
        parseFile(path);
      }
    } else {
      File[] files = path.listFiles();
      if (files != null) {
        for (File file : files) {
          walkFile(file);
        }
      }
    }
  }

  private static void parseFile(File file) throws Exception {
    try {
      System.out.println(file.getAbsolutePath());
      语法解析器.setSource(FileUtil.getStringFromSourceFile(file)
          .toCharArray());
    } catch (Exception e) {
      assertTrue(false);
    }
    语法解析器.createAST(null).accept(提取器);
    类型结构 结构 = 提取器.获取类结构();
    生成(结构);
  }
  
  private static void 生成(类型结构 结构) throws IOException, ClassNotFoundException {
    // TODO: 取源包路径
    String 包路径 = "爪哇";

    // TODO: 处理无方法接口、类，如：java/lang/annotation/Inherited.java
    TypeDeclaration 源类;
    if (结构.类型.size() == 1) {
      源类 = 结构.类型.get(0);
    } else {
      return;
    }

    List<MethodDeclaration> 方法 = 结构.方法;
    
    CompilationUnit cu = ast.newCompilationUnit();
    PackageDeclaration p1 = ast.newPackageDeclaration();
    p1.setName(ast.newName(包路径));
    cu.setPackage(p1);
    
    TypeDeclaration td = ast.newTypeDeclaration();
    td.setName(ast.newSimpleName(源类.getName().getFullyQualifiedName()));
    TypeParameter tp = ast.newTypeParameter();
    tp.setName(ast.newSimpleName("X"));
    td.typeParameters().add(tp);
    cu.types().add(td);
        
    // 封装源类的一个对象
    SingleVariableDeclaration sourceObject = ast.newSingleVariableDeclaration();
    sourceObject.setName(ast.newSimpleName("对象"));
    // TODO: add it to class field
    
    // TODO: 构造方法，依赖于源类的构造方法
    
    for (MethodDeclaration 方法之一 : 方法) {
      
      MethodDeclaration md = ast.newMethodDeclaration();
      md.setName(ast.newSimpleName(方法之一.getName().getFullyQualifiedName()));
      md.modifiers().addAll(ast.newModifiers(方法之一.getModifiers()));

      Type type = 方法之一.getReturnType2();
      try {
        md.setReturnType2(createReturnType(type));
      } catch (Exception e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
      }

      td.bodyDeclarations().add(md);

      Block block = ast.newBlock();
      md.setBody(block);

      MethodInvocation mi = ast.newMethodInvocation();
      mi.setName(ast.newSimpleName("x"));

      ExpressionStatement e = ast.newExpressionStatement(mi);
      block.statements().add(e);
    }

    // System.out.println(cu);
  }

  // 获取方法返回类型
  private static Type createReturnType(Type type) throws Exception {
    if (type instanceof PrimitiveType) {
      return ast.newPrimitiveType(((PrimitiveType) type).getPrimitiveTypeCode());
    } else if (type instanceof SimpleType) {
      return createSimpleType(type);
    } else if (type instanceof ArrayType) {
      return ast.newArrayType(createReturnType(((ArrayType) type).getElementType()));
    } else if (type instanceof QualifiedType) {
      // TODO: not found in java/, may still be issue
      System.out.println(type);
      return null;
    } else if (type instanceof ParameterizedType) {
      ParameterizedType newType =
          ast.newParameterizedType(createReturnType(((ParameterizedType) type).getType()));
      List<Type> typeArguments = ((ParameterizedType) type).typeArguments();
      for (Type typeArgument : typeArguments) {
        newType.typeArguments().add(createReturnType(typeArgument));
      }
      return newType;
    } else if (type instanceof WildcardType) {
      WildcardType newType = ast.newWildcardType();
      Type boundType = ((WildcardType) type).getBound();
      if (boundType == null) {
        return newType;
      } else {
        // TODO: not found in java/, may still be issue
        return null;
      }
    } else {
      // shouldn't reach here
      return null;
    }
  }

  private static Type createSimpleType(Type type) {
    try {
      Name name = ((SimpleType) type).getName();
      String qualifiedName = name.getFullyQualifiedName();
      Type newType = null;
      if (name instanceof SimpleName) {
        newType = ast.newSimpleType(ast.newSimpleName(qualifiedName));
      } else if (name instanceof QualifiedName) {
        // 测试例子：java/awt/image/BufferedImage.java， java.awt.Graphics getGraphics()
        String[] names = qualifiedName.split("\\.");
        if (names == null || names.length == 0) {
          return null;
        } else {
          newType = ast.newSimpleType(ast.newSimpleName(names[0]));
          if (names.length > 1) {
            for (int i = 1; i < names.length; i++) {
              newType = ast.newQualifiedType(newType, ast.newSimpleName(names[i]));
            }
          }
        }
      }
      return newType;
    } catch (IllegalArgumentException e) {
      System.out.println(e.getLocalizedMessage());
      return null;
    }
  }
}
